package com.sf.proxy.intercept.common;

import com.sf.proxy.chain.ChainContext;
import com.sf.proxy.chain.ProxyUrlCollectorChain;
import com.sf.proxy.intercept.HttpProxyIntercept;
import com.sf.proxy.intercept.HttpProxyInterceptPipeline;
import com.sf.proxy.proxy.RequestProto;
import com.sf.proxy.util.AsycUtils;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class FullResponseIntercept extends HttpProxyIntercept {

  //default max content length size is 8MB
  private static final int defaultMaxContentLength = 1024 * 1024 * 8;

  private int maxContentLength;

  public FullResponseIntercept() {
    this(defaultMaxContentLength);
  }

  public FullResponseIntercept(int maxContentLength) {
    this.maxContentLength = maxContentLength;
  }

  @Override
  public final void afterResponse(Channel clientChannel, Channel proxyChannel,
                                  HttpResponse httpResponse,
                                  HttpProxyInterceptPipeline pipeline) throws Exception {
    if (httpResponse instanceof FullHttpResponse) {
      FullHttpResponse fullHttpResponse = (FullHttpResponse) httpResponse;
      handelResponse(pipeline.getHttpRequest(), fullHttpResponse, pipeline);
      if (fullHttpResponse.headers().contains(HttpHeaderNames.CONTENT_LENGTH)) {
        httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, fullHttpResponse.content().readableBytes());
      }
      proxyChannel.pipeline().remove("decompress");
      proxyChannel.pipeline().remove("aggregator");
    } else if (match(pipeline.getHttpRequest(), pipeline.getHttpResponse(), pipeline)) {
      pipeline.resetAfterHead();
      proxyChannel.pipeline().addAfter("httpCodec", "decompress", new HttpContentDecompressor());
      proxyChannel.pipeline()
          .addAfter("decompress", "aggregator", new HttpObjectAggregator(maxContentLength));
      proxyChannel.pipeline().fireChannelRead(httpResponse);
      return;
    }
    FullHttpRequest request =pipeline.getOriginRequest();
    HttpResponse response = pipeline.getHttpResponse();
    RequestProto requestProto = pipeline.getRequestProto();
    AsycUtils.execute(new CollectUrlTask(request,response,requestProto,clientChannel));
    pipeline.afterResponse(clientChannel, proxyChannel, httpResponse);
  }

  protected boolean isHtml(HttpRequest httpRequest, HttpResponse httpResponse) {
    String accept = httpRequest.headers().get(HttpHeaderNames.ACCEPT);
    String contentType = httpResponse.headers().get(HttpHeaderNames.CONTENT_TYPE);
    return httpResponse.status().code() == 200 && accept != null && accept
        .matches("^.*text/html.*$") && contentType != null && contentType
        .matches("^text/html.*$");
  }

  /**
   * 匹配到的响应会解码成FullResponse
   */
  public abstract boolean match(HttpRequest httpRequest, HttpResponse httpResponse,
                                HttpProxyInterceptPipeline pipeline);

  /**
   * 拦截并处理响应
   */
  public abstract void handelResponse(HttpRequest httpRequest, FullHttpResponse httpResponse,
                                      HttpProxyInterceptPipeline pipeline);

  private class CollectUrlTask implements Runnable{
    private HttpRequest request;
    private HttpResponse response;
    private RequestProto requestProto;
    private Channel clientChannel;

    public CollectUrlTask(HttpRequest request,HttpResponse response,RequestProto requestProto,Channel clientChannel){
      this.request=request;
      this.response = response;
      this.requestProto = requestProto;
      this.clientChannel = clientChannel;
    }

    @Override
    public void run() {
        log.info("start to handle the full request ...");
        if (request ==null||!(request instanceof FullHttpRequest)){
          log.warn("the request is not correct ...");
          return;
        }
        FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
        ChainContext context = ChainContext.getInstance().buildRequest(fullHttpRequest).buildRespoense((response)).buildClientChannel(clientChannel).buildRequestProto(requestProto);
        ProxyUrlCollectorChain.getInstance().execute(context);

    }
  }

}
